﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Threading;
using System.ServiceModel;

namespace Aliyeye.Car.Net.UI
{
    internal static class ServiceManipulator
    {
        /// <summary>
        /// Create a service and hook the closing event of containing form, close the created proxy when form closed.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="owner"></param>
        /// <returns></returns>
        public static T CreateService<T>(System.Windows.Forms.Form owner) where T : System.ServiceModel.ICommunicationObject, new ()
        {

            T ret = Activator.CreateInstance<T>();
            owner.FormClosing += new System.Windows.Forms.FormClosingEventHandler(delegate(object sender, System.Windows.Forms.FormClosingEventArgs e)
            {
                if (ret != null && ret.State != System.ServiceModel.CommunicationState.Faulted)
                {
                    ret.CloseConnection();
                }
            });

            return ret;
        }
        
        /// <summary>
        /// Create proxy object and close it when async operation successed.
        /// </summary>
        /// <typeparam name="ExtensionType"></typeparam>
        /// <param name="asyncCallback"></param>
        /// <param name="doneCallback"></param>
        public static void SafeAsyncExecution<ExtensionType>(Func<ExtensionType, Object> asyncCallback, 
            TCallback doneCallback, 
            System.Windows.Forms.Control doneCallbackContext) 
            where ExtensionType : ICommunicationObject, new()
        {
            if (doneCallback == null)
            {
                throw new Exception("No callback specified.");
            }

            ExtensionType ret = Activator.CreateInstance<ExtensionType>();

            CallbackResult result = new CallbackResult();
            try
            {
                asyncCallback.BeginInvoke(ret, new AsyncCallback(delegate(IAsyncResult iaResult){
                    try
                    {
                        result.Result = asyncCallback.EndInvoke(iaResult);
                    }
                    catch (Exception ex)
                    {
                        result.Error = ex;
                    }

                    try
                    {
                        doneCallbackContext.Invoke(doneCallback, result);
                    }
                    catch (InvalidOperationException)
                    {
                    }
                        // do not catch other exception
                        // leave the exception to be handled in donecallback
                    finally
                    {
                        ret.CloseConnection();
                    }

                    if (result.Error != null && result.isErrorHandled == false)
                    {
                        throw result.Error;
                    }

                }), null);
            }
            catch (Exception ex)
            {
                // pass the exception to doneCallback in order to be processed
                result.Error = ex;

                try
                {
                    doneCallbackContext.Invoke(doneCallback, result);
                }
                finally
                {
                    ret.CloseConnection();
                }

                if (!result.isErrorHandled)
                {
                    throw result.Error;
                }
            }

            

        }

        public delegate void TCallback(CallbackResult result);
        public class CallbackResult 
        {
            public Object Result = null;

            private Exception _error = null;

            internal bool isErrorHandled = false;
            public Exception Error 
            {
                get
                {
                    isErrorHandled =true;
                    return _error;
                }
                set
                {
                    isErrorHandled=true;
                    _error = value;
                }
            }
        }

        /// <summary>
        /// Safely closes a service client connection.
        /// </summary>
        /// <param name="myServiceClient">The client connection to close.</param>
        public static void CloseConnection(this ICommunicationObject myServiceClient)
        {

            try
            {
                myServiceClient.Close();
            }
            catch (CommunicationException ex)
            {
                //Debug.Print(ex.ToString());
                myServiceClient.Abort();
            }
            catch (TimeoutException ex)
            {
                //Debug.Print(ex.ToString());
                myServiceClient.Abort();
            }
            catch (Exception ex)
            {
                //Debug.Print(ex.ToString());
                myServiceClient.Abort();
                throw;
            }
        }
    }

}
